Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: slashing release #679

Open
wants to merge 1 commit into
base: custom-errors
Choose a base branch
from
Open

Conversation

8sunyuan
Copy link
Collaborator

@8sunyuan 8sunyuan commented Aug 12, 2024

Updated 9/20/24

Contract Descriptions

DONT REVIEW THE REWARDS COORDINATOR

AVSDirectory

  • The OperatorSets release has been combined into the Slashing release. This includes interfaces for AVS migration to operatorSets and register/deregister functionality.
  • Slashing and magnitude allocations revolve around the introduction of these operatorSets. More on this below...

New Core contract: AllocationManager! (basically the Slasher contract)

  • This is the Slasher contract, pending decision on rename or just changing back to Slasher.
  • This implements "unique security", stake is not reused across AVSs/operatorSets and operators allocate slashable proportions to each operatorSet. Imagine this as a pie chart where each slice gets given to a different operatorSet.
  • modifyAllocations can be called to configure updated allocations for a strategy per opSet. Allocations/deallocations are handled differently in the underlying storage.
  • slashOperator, from the perspective of an (operator, Strategy) tuple, slashes from current magnitude as well as queued deallocations. Whatever magnitude is slashed is also decremented from the totalMagnitude from the (operator, Strategy) tuple.
  • Deallocations are slashable while pending. Pending allocations on the other hand are not slashable (referring to the increased amount) because allocations are increases that are also decrementing from the non-allocated/non-slashable magnitude. We refer to this as freeMagnitude in storage.
  • Deallocations are all on a fixed 17.5 day delay
  • Allocation delays are configurable on a per operator basis. Updating the allocationDelay has a delay for it to take effect itself. Open PR for this is here. feat: allocation delay #740

StrategyManager/EigenPodManager/DelegationManager - Changes to Deposits/Withdrawals

This code comment explains a bunch:

   /*
 * There are 3 types of shares:
 *      1. ownedShares
 *          - These can be converted to an amount of tokens given a strategy
 *              - by calling `sharesToUnderlying` on the strategy address (they're already tokens 
 *              in the case of EigenPods)
 *          - These are comparable between operators and stakers.
 *          - These live in the storage of StrategyManager strategies: 
 *              - `totalShares` is the total amount of shares delegated to a strategy
 *      2. delegatedShares
 *          - These can be converted to shares given an operator and a strategy
 *              - by multiplying by the operator's totalMagnitude for the strategy
 *          - These values automatically update their conversion into tokens
 *              - when the operator's total magnitude for the strategy is decreased upon slashing
 *          - These live in the storage of the DelegationManager:
 *              - `delegatedShares` is the total amount of delegatedShares delegated to an operator for a strategy
 *              - `withdrawal.delegatedShares` is the amount of delegatedShares in a withdrawal          
 *      3. shares 
 *          - These can be converted into delegatedShares given a staker and a strategy
 *              - by multiplying by the staker's depositScalingFactor for the strategy
 *          - These values automatically update their conversion into tokens
 *             - when the staker's depositScalingFactor for the strategy is increased upon new deposits
 *             - or when the staker's operator's total magnitude for the strategy is decreased upon slashing
 *          - These represent the total amount of shares the staker would have of a strategy if they were never slashed
 *          - These live in the storage of the StrategyManager/EigenPodManager
 *              - `stakerStrategyShares` in the SM is the staker's principalShares that have not been queued for withdrawal in a strategy
 *              - `podOwnerShares` in the EPM is the staker's principalShares that have not been queued for withdrawal in the beaconChainETHStrategy
 */
  • Minimal changes to SM/EPM since the
  • Withdrawal struct in the DelegationManager has been changed from startBlock to startTimestamp. To account for legacy M2 withdrawals, we check that this field is less than a specific timestamp and handle accordingly.
  • DM: EIGEN strategy delay is currently set to 7 days so all the strategies currently have the same delay. We remove strategy specific delays entirely here but all withdrawals will be on a 17.5 day delay (same as deallocation delay)
  • DM: We've abstracted the EPM and SM behind a unified IShareManager interface
  • Accounting NOTEs:
    • depositScalingFactor is the k value used in the accounting docs
    • stakerStrategyShares is s
    • operatorDelegatedShares is op
    • totalMagnitude is m read from the AllocationManager

Misc Notes:

  • Using OZ CheckpointsUpgradeable library in the AllocationManager to keep track of historically timestamped magnitude values. This is required for staker withdrawal completion where the timestamped totalMagnitude value at completion may not be the current so we have a lookup for that. We renamed the Checkpoints library to Snapshots to avoid confusion with EigenPod checkpoints.
  • 0.8.27 custom errors with requires (saves a lot of bytecode size)
  • thirdPartyTransfersForbidden is removed entirely. This mapping will be deprecated and never read from again.
  • We will likely deploy with optimizer-runs = 0-10 to save on contract size
  • DONT LOOK AT SCRIPTS OR TESTS YET

TODOs:

  • burning of slashed shares: requires scoping
  • BytecodeSize (if applicable)
  • EigenPodManager stuff: beaconchain slashing, negative shares

Deposits/Withdraws of Shares [WIP]

  • Confirm no overflow scenarios as part of scaling factor calculations in deposits/withdraws. Note that permissionless staking limits the number of shares in Strategies
  • Events

Allocator Configuration [WIP]

  • Figure out a tolerable rounding scheme for slashing magnitude allocations
  • Events

This comment was marked as spam.

@Layr-Labs Layr-Labs deleted a comment from github-actions bot Aug 19, 2024
@Layr-Labs Layr-Labs deleted a comment from github-actions bot Aug 19, 2024
src/contracts/core/AVSDirectory.sol Fixed Show fixed Hide fixed
src/contracts/core/AVSDirectory.sol Fixed Show fixed Hide fixed
src/contracts/core/AVSDirectory.sol Fixed Show fixed Hide fixed
Copy link
Collaborator

@ypatil12 ypatil12 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review on alloc/dealloc

src/contracts/core/AVSDirectory.sol Outdated Show resolved Hide resolved
src/contracts/core/AVSDirectory.sol Outdated Show resolved Hide resolved
src/contracts/core/AVSDirectory.sol Outdated Show resolved Hide resolved
src/contracts/core/AVSDirectory.sol Outdated Show resolved Hide resolved
src/contracts/core/AVSDirectory.sol Outdated Show resolved Hide resolved
src/contracts/core/AVSDirectory.sol Outdated Show resolved Hide resolved
src/contracts/core/AVSDirectory.sol Outdated Show resolved Hide resolved
src/contracts/core/AVSDirectory.sol Outdated Show resolved Hide resolved
src/contracts/core/AVSDirectory.sol Outdated Show resolved Hide resolved
src/contracts/core/AVSDirectory.sol Outdated Show resolved Hide resolved
@MinisculeTarantula
Copy link
Collaborator

require allocationDelay being set when calling modifyAllocations. Based on current implementation of a default allocation delay, if a shorter delay is set, it is possible to create a pending magnitudeUpdate that has a timestamp thats earlier than a already existing pending update. We need the checkpoints history to be asc sorted and figuring out some insertion method instead of pushing the history seems like too much overhead complexity.

Would it be bad / hard / undesirable to enforce this ordering on the checkpoints history level?

I would suggest that if an attempt is made to push an entry with a timestamp that is earlier than the timestamp of the previous entry, either (a) it's simply not allowed or (b) the new entry has its timestamp modified to match (or be 1 higher? not sure if the ascending ordering you have is strict or non-strict).

Perhaps some option (c) could work where you keep the original timestamp but overwrite the intentions of the other entry? IDK if that would be incompatible with other aspects of the storage model though.

I was initially thinking option (b) was the most logical but modifying entries before pushing them can be messy, especially if the entry or its contents is used elsewhere (e.g. if the timestamp is emitted in events, its hard to make sure the event emits the correct timestamp when you have conditional logic for modifying it).

Copy link
Collaborator

@ypatil12 ypatil12 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor slashing comments

src/contracts/core/AVSDirectory.sol Outdated Show resolved Hide resolved
{
uint256 queuedDeallocationIndicesLen =
_queuedDeallocationIndices[operator][strategies[i]][msg.sender][operatorSetId].length;
for (uint256 j = queuedDeallocationIndicesLen; j > 0; --j) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since MAX_PENDING_UPDATES is 1, we don't need a loop here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gpsanant mentioned we want to check without assuming max 1 and be able to update in the future to change MAX_PENDING_UPDATES

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eh

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gpsanant Should I remove the loop?

@ypatil12 ypatil12 mentioned this pull request Aug 29, 2024
@ypatil12 ypatil12 changed the base branch from feat/operator-set-release to dev August 29, 2024 20:24
Copy link
Collaborator

@wadealexc wadealexc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need pausing on AllocationManager methods

Edit: fixed in #786

@0xClandestine
Copy link
Contributor

We need pausing on AllocationManager methods

Added in #786

Comment on lines 67 to 71
struct MagnitudeInfo {
int128 pendingMagnitudeDiff;
uint64 currentMagnitude;
uint32 effectTimestamp;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a fan of ordering the fields like this -- IMO unless absolutely needed for storage packing, fields should be logically grouped by function. So, currentMagnitude should not sit between a pending diff and its effective timestamp.

@wadealexc wadealexc force-pushed the slashing-magnitudes branch 2 times, most recently from f62d94e to 069c669 Compare October 4, 2024 15:06
Comment on lines 286 to 287
// update the magnitude info in storage
_operatorMagnitudeInfo[operator][strategy][operatorSetKey] = mInfo;
Copy link
Collaborator

@wadealexc wadealexc Oct 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You update this here, but also in 2/3 places where _completePendingModification is called

* encoding of the operatorSet. This is to prevent duplicate operatorSets being passed in. The easiest way to ensure
* ordering is to sort allocated operatorSets by address first, and then sort for each avs by ascending operatorSetIds.
*/
function modifyAllocations(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we include a signature based variant, so anyone can modify allocations on behalf of an operator?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed because it was bloating code and we are adding delegated accounts

wdyt @ypatil12 @wadealexc ?


// If we've reached a pending modification that isn't completable yet,
// we can stop. Any subsequent modificaitons will also be uncompletable.
if (block.timestamp < info.effectTimestamp) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check diff != 0

function _completePendingModification(
struct PendingMagnitudeInfo {
// Current magnitude allocated to this operatorSet
uint64 currentMagnitude;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

abstract this as a nested MagnitudeInfo

Comment on lines 108 to 99
function registerAsOperator(
OperatorDetails calldata registeringOperatorDetails,
uint32 allocationDelay,
string calldata metadataURI
) external {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are comfortable changing this interface, let's make it a little more sane and use a new version of the OperatorDetails struct that includes an allocationDelay and does not have deprecated fields

/**
* @notice Original EIP-712 Domain separator for this contract.
* @dev The domain separator may change in the event of a fork that modifies the ChainID.
* Use the getter function `domainSeparator` to get the current domain separator for this contract.
*/
bytes32 internal _DOMAIN_SEPARATOR;
bytes32 internal __deprecated_DOMAIN_SEPARATOR;

Check warning

Code scanning / Slither

State variables that could be declared constant

AVSDirectoryStorage.__deprecated_DOMAIN_SEPARATOR (src/contracts/core/AVSDirectoryStorage.sol#49) should be constant
Comment on lines +42 to +45
modifier onlyEigenPodManager() {
require(msg.sender == address(eigenPodManager), OnlyEigenPodManager());
_;
}

Check notice

Code scanning / Slither

Incorrect modifier

Modifier DelegationManager.onlyEigenPodManager() (src/contracts/core/DelegationManager.sol#42-45) does not always execute _; or revert

// Mutatables

bytes32 internal __deprecated_DOMAIN_SEPARATOR;

Check warning

Code scanning / Slither

State variables that could be declared constant

DelegationManagerStorage.__deprecated_DOMAIN_SEPARATOR (src/contracts/core/DelegationManagerStorage.sol#68) should be constant
* Use the getter function `domainSeparator` to get the current domain separator for this contract.
*/
bytes32 internal _DOMAIN_SEPARATOR;
bytes32 internal __deprecated_DOMAIN_SEPARATOR;

Check warning

Code scanning / Slither

State variables that could be declared constant

RewardsCoordinatorStorage.__deprecated_DOMAIN_SEPARATOR (src/contracts/core/RewardsCoordinatorStorage.sol#63) should be constant

// Mutatables

bytes32 internal __deprecated_DOMAIN_SEPARATOR;

Check warning

Code scanning / Slither

State variables that could be declared constant

StrategyManagerStorage.__deprecated_DOMAIN_SEPARATOR (src/contracts/core/StrategyManagerStorage.sol#39) should be constant
StakerScalingFactors memory ssf
) internal pure returns (uint64) {
return
!ssf.isBeaconChainScalingFactorSet && ssf.beaconChainScalingFactor == 0 ? WAD : ssf.beaconChainScalingFactor;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be simplified to:

return ssf.isBeaconChainScalingFactorSet ? ssf.beaconChainScalingFactor : WAD;

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Followup: the code seems to imply that it's possible for beaconChainScalingFactor to equal 0. However, this would cause mulDiv to revert if it's used as the denominator!

If this can be zero, we need to fix that.

If this can't be zero, this code is incorrect.


// For allocations, immediately add to encumberedMagnitude to ensure the operator
// can't allocate more than their maximum
info.encumberedMagnitude = _addInt128(info.encumberedMagnitude, info.pendingDiff);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this need to be decremented as well in the case of deallocations?
Right now when we call _updateMagnitudeInfo we are passing in info which has the stale value of encumberedMagnitude.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wrong, deallocations decrement from encumberedMagnitude when they are cleared which makes sense. The pending diff can change depending if the deallocation was slashed or not.

// 2. If there is a pending deallocation, reduce pending deallocation proportionally.
// This ensures that when the deallocation is completed, less magnitude is freed.
if (info.pendingDiff < 0) {
uint64 slashedPending = uint64(uint256(uint128(-info.pendingDiff)).mulWad(params.wadToSlash));
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

slashedPending is not used anywhere else so can we remove this uint64 cast?

TomasArrachea added a commit to Layr-Labs/eigensdk-rs that referenced this pull request Oct 16, 2024
Added this rewards methods to ElContracts ChainWriter:
- `set_claimer_for`
- `process_claim`

Still missing this methods (they are still not merged on dev branch of
eigenlayer-contracts - will be merged with the [slashing
PR](Layr-Labs/eigenlayer-contracts#679)):
- `ForceDeregisterFromOperatorSets`
- `SetOperatorCommissionBips`
@ypatil12 ypatil12 force-pushed the custom-errors branch 2 times, most recently from db2ccda to 153d625 Compare October 17, 2024 06:45
@ypatil12 ypatil12 force-pushed the slashing-magnitudes branch 5 times, most recently from eabea03 to 25617f0 Compare October 17, 2024 07:20
chore: forge fmt src/contracts

fix: ci and bindings

chore: dmgr error tweaks

chore: error tweaks for consistency and clarity

feat: bump oz version (#755)

* feat: bump oz version -> 0.4.9

- also moved remappings -> foundry.toml
- removes remappings.txt

* bindings

---------

Co-authored-by: gpsanant <[email protected]>

test: custom errors passing (#783)

* test: custom errors AVSDir

* test: custom errors IPausable

* test: custom errors Delegation

* test: custom errors EigenPodManager

* test: custom errors EigenPod

* test: custom errors Pausable

* test: custom errors RewardsCoordinator

* test: custom errors IStrategy

* test: custom errors StrategyManager

* test: custom errors DelegationManager

* test: custom errors

refactor: review reconciliation

refactor: review reconciliation

refactor: review reconciliation

chore: forge fmt src/contracts

feat: slashing

* chore: pending delay calc cleanup

* chore: storage pointer cleanup

* eigenpods slashing updates (#745)

* squash yet again

* change again

* update checkpoint struct

* feat: AllocationManager Storage Simplification  (#787)

* feat: cleanup

* feat: add helper func

* fix: simplification

* chore: clean up magnitude info usage and type conversions

* refactor: review changes

* fix: order struct params by size

* fix: correct and simplify calc for slashed pending magnitude

* fix: storage gap

* feat: cleanup

* chore: remove some type conversion bs and minor formatting

* chore: clean up free magnitude logic

* chore: rename pending deallocations and fix stack too deep

* feat: slashing magnitudes cleanup (#786)

* refactor: slashing magnitudes cleanup
* refactor: assert `bipToSlash` is bounded
* assert `bipsToSlash` is lte 100% and gt 0%.

* refactor: move `isOperatorSlashable` -> AVSD

* refactor: batch validate opsets on allocations

- adds `AVSD.isOperatorSetBatch(operatorSets)`

* feat: add pausing to ALM

* refactor: remove single use helper

- removes `_getLatestTotalMagnitude(operator, strategy)`

* refactor: rename `ALLOCATION_DELAY_CONFIGURATION_DELAY`

* refactor: remove `Slasher`

* refactor: move constants + immutables to storage contracts

* refactor: custom errors `RewardsCoordinatorStorage`

* chore: dependency cleanup

* fix: remove unused internal getter

* chore: batch validate operator sets and minor cleanup

* fix: fix stack too deep and compiler errors

---------

Co-authored-by: wadealexc <[email protected]>

feat: dm cleanup (#788)

Co-authored-by: wadealexc <[email protected]>

Revert "feat: dm cleanup (#788)" (#799)

This reverts commit c27004e.

fix: compiles (#800)

fix: refactor (#801)

* fix: refactor

* default was history

* reline

* rename

rename

test: generally compiling + AVSM unit tests compiling

chore: forge fmt src/contracts

add events, fix bugs, abstract better (#806)

* fix bugs, add events, cleanup

* wrap conditional

* fmt

* only one slash per timestamp

test(wip): generally compiling

update docs (#804)

fix: revert change to event

feat: am refactor

add to wads to slash

cleanup

refactor: change totalMagnitude to maxMagnitude
* condense slashOperator params
* some struct field renaming

remove unused eigenpod storage

chore: storage report (#809)

* chore: storage report

* patch eigenpod

---------

Co-authored-by: gpsanant <[email protected]>

feat: eip712 fixes (#808)

* feat: use OZ SignatureChecker

* feat: add `SignatureUtils` mixin

* refactor: cleanup

* feat: make storage report

* storage report

---------

Co-authored-by: gpsanant <[email protected]>

test: slashing tests passing (#812)

fix: merge issues

update events

refactor: rename total magnitudes to max magnitudes
* various formatting and cleanup
* standardize allocation manager getter functions
* update and improve commenting

refactor: reorder functions to match interface

fix: remove memory overwrite bug in delegation manager

chore: forge fmt

refactor: clean up getDepositedShare logic

chore: remove old oz + forge update foundry-rs/forge-std

feat: replace getSlashableMagnitudes with general purpose allocation info query

Feat: SM/StrategyBase Unit Tests + Formatting (#813)

refactor: delegation manager refactors

test: AllocationManager progress

feat: change event names

feat: update doc

fix: compile

test: AllocationManager progress

fix: tests progress

add Strategy <> OperatorSet mapping in storage, and APIs and events (#814)

* feat: add strategy to operator set mapping with corresponding functions and events

* fix: update

* fix: remove pagination of getStrategiesInOperatorSet

* fix: update

* fix: compiles

* fix: add checks

* fix: address -> IStrategy

* fix: storage gap

---------

Co-authored-by: gpsanant <[email protected]>

Slashing: DM Happy Path Test Cases (#815)

* test: basic dm tests

* test: start on share increase/decrease delegated shares tests

* test: add DM unit tests passing except queue/complete

* test: undelegate tests

* test: queue withdrawals

* test: completed DM happy path test cases

* fix: compilation, rebase errors

* chore: format

Add view funcs (#816)

* fix: add new view funcs

* fix: update docs

test: fix avsD tests (#817)

chore: format

fix: from scratch deploy

feat: add shares to slashing event

Slashing: Modify Allocations Happy Path Tests (#821)

* refactor: add test contract per func

* test: modify allocations

* chore: format

slashing: allocation delay happy path tests (#822)

feat: wadSlashed (#820)

Slashing: Clear Modification Queue Happy Path Tests (#823)

test: basic allocation tests (#824)

feat: inheritdoc

refactor: alm test cleanup

test: multiple allocations, only one slashed

test: one pending alloc, slash

test: revert bound refactor so tests pass

Slashing: Add additional happy path AM test cases (#828)

* test: happy path AM tests

* chore: format

Slashing: Get all tests passing (#829)

* test: all tests passing

* fix: mainnet integration test comment out

Fix misset storage gaps (#831)

* fix: misset storage gaps from #814

* fix: update gap to account for previous refactor

fix: update coverage yml name (#833)

Fix: Single Deallocation Queue (#827)

* test: base regression

* test: regression

* fix: remove console

* test: logs

* test: add actual regression

* fix: use a single deallocation queue

* fix: comments

* refactor: use deallocation queue everywhere

* fix: address test comments

* fix: test comment

Feat: Update legacy withdrawal timestamp param to legacy withdrawal check (#836)

* fix: make comment on timestamp clearer

* chore: format

Feat: Public Devnet Deploy (#838)

* feat: slashing public devnet

* fix: filepath

* test: validation

* fix: add test

* fix: test

fix: compile

chore: format
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants